package ls.vuecp.gui.component;

import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import tufts.Util;
import tufts.vue.DEBUG;

public abstract class LWContainer extends LWComponent
{
	private static final Object REMOVE_DEFAULT = "default";
    private static final Object REMOVE_DELETE = "delete";
    
	public LWContainer()
	{

	}

    protected java.util.List<LWComponent> mChildren = NO_CHILDREN;
    /** If all the children do not have the same parent, the sort order won't be 100% correct. */
    public static final Comparator ZOrderSorter = new Comparator<LWComponent>() {
            public int compare(LWComponent c1, LWComponent c2) {
                final LWContainer parent1 = c1.getParent();
                final LWContainer parent2 = c2.getParent();

                // We can't get z-order on a node if it's an orphan (no
                // parent), which is what any paste's or system drags
                // will get us.  So we'll need to keep a sync'd a z-order
                // value in LWComponent to support this in all cases.

                if (parent1 == parent2)
                    return parent1.getChildren().indexOf(c1) - parent2.getChildren().indexOf(c2);
                else
                    return 0;
                // it's possible to figure out which parent is deepest,
                // but we'll save that for later.
            }
        };
    @Override
    public java.util.List<LWComponent> getChildren()
    {
        return mChildren;
    }
    public void addChildren(LWComponent[] toAdd)
    {
        addChildren(Arrays.asList(toAdd), ADD_PRESORTED);
    }
   
    @Override
    public void addChildren(Collection<? extends LWComponent> toAdd, Object context)
    {
       if (toAdd == null || toAdd.size() < 1)
            return;
        
//        notify(LWKey.HierarchyChanging);

        if (mChildren == NO_CHILDREN)
            mChildren = new ArrayList(toAdd.size());

        if (toAdd.size() > 1 && context != ADD_PRESORTED)
            toAdd = sortForIncomingZOrder(toAdd);
        
        final List<LWComponent> added = new ArrayList(toAdd.size());

        final long timestamp = System.currentTimeMillis();

        for (LWComponent c : toAdd) {
            try {
                if (c.getCreated() == 0)
                    c.setCreated(timestamp);
                
                addChildImpl(c, context);
                added.add(c);
            } catch (Throwable t) {
                Log.error(this + "; addChildImpl: " + c + ";", t);
            }
        }

//        notify(LWKey.ChildrenAdded, added);
        
//        layout();
    }
    protected void addChildImpl(LWComponent c, Object context)
    {
        if (c == this) 
            throw new Error("attempt to add self as child");

//        if (context == ADD_MERGE && c.hasAncestor(this))
//            return;
//
//        if (c.getParent() != null && c.getParent().hasChild(c)) {
//            if (c.getParent() == this) {
//                if (DEBUG.PARENTING) out("ADD-BACK " + c + " (already our child)");
//            }
//            c.getParent().removeChild(c); // is LWGroup requesting cleanup???
//        }

        if (mChildren == NO_CHILDREN)
            mChildren = new ArrayList();
        
        if (context.getClass() == Integer.class) {
            mChildren.add( (Integer)context, c);
        } else {
            mChildren.add(c);
        }
        
        setAsChildAndLocalize(c);

//        ensureID(c);

//        c.notifyHierarchyChanged();
        
    }
    protected void setAsChildAndLocalize(LWComponent c) {
        
        final LWContainer oldParent = c.getParent();
        
//        if (oldParent != null && !isManagingChildLocations()) {
//
//            // If we're managing child locations (e.g., and LWNode), no need
//            // to localize, as the node will have it's position explicitly assigned
//            // by the parent when it lays itself out
//			
//        	// Save current mapX / mapY before setting the parent (which would change the reported mapX / mapY)
//        	final float oldMapX = c.getMapX();
//            final float oldMapY = c.getMapY();
//            final double oldParentMapScale = c.getMapScale();
//
//            if (false) {
//                localizeCoordinates(c, oldParent, oldParentMapScale, oldMapX, oldMapY);
//                c.setParent(this);
//            } else {
//        
//                // Now set the parent, so that when the new location is set, it's already in it's
//                // new parent, and it's mapX / mapY will report correctly when asked (e.g., the
//                // bounds are immediatley correct for anyone listening to the location event).
//                c.setParent(this);
//                try {
//                    localizeCoordinates(c, oldParent, oldParentMapScale, oldMapX, oldMapY);
//                } catch (Throwable t) {
//                    Util.printStackTrace(t);
//                }
//            }
//            
//        } else {
            
            c.setParent(this);
            
//        }
        
    }

    protected Collection<? extends LWComponent> sortForIncomingZOrder(Collection<? extends LWComponent> toAdd)
    {
        if (toAdd.size() == 0)
            return Collections.EMPTY_LIST;

        Collection<? extends LWComponent> sorted;

        if (Util.getFirst(toAdd).getParent() == null) {

            sorted = toAdd;
        } else {
            sorted = Arrays.asList(sort(toAdd, ZOrderSorter));
        }


        return sorted;
    }
    protected static LWComponent[] sort(Collection<? extends LWComponent> bag, Comparator comparator)
    {
        LWComponent[] array = new LWComponent[bag.size()];
        bag.toArray(array);
        java.util.Arrays.sort(array, comparator);
        return array;
    }
}
